/**HEADER********************************************************************
* 
* Copyright (c) 2008-2011 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2010 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: timer_e300dec.c$
* $Version : 3.8.3.1$
* $Date    : Feb-13-2012$
*
* Comments:
* 
*    This file contains definitions for the PPC e300 core decrementer
*    timer utility functions.
*
*END***********************************************************************/


#include <mqx_inc.h>
#include <bsp.h>
#include "timer_e300dec.h"


/* Default to sw decremeter reload if MQX makes use of hw ticks */
#ifndef BSP_E300DEC_SW_RELOAD
#define BSP_E300DEC_SW_RELOAD MQX_HAS_HW_TICKS
#endif


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _e300_decrementer_init_freq
* Returned Value  : hw ticks per tick
* Comments        :
*   this function will set up e300 decrementer
*
*END*---------------------------------------------------------------------*/

uint_32 _e300_decrementer_init_freq
    (
        /* [IN] ticks per second */
        uint_32   tickfreq,

        /* [IN] decrementer input clock speed in Hz */
        uint_32   clk
    )
{
    uint_32 period, hid0;

    period = (clk/tickfreq + 2 ) / 4;

    /* set auto-reload value */
    _PSP_SPR_SET(E300CORE_DEC, (period-1));

#if BSP_E300DEC_SW_RELOAD != 1
    _PSP_SPR_GET(hid0, E300CORE_HID0);
    hid0 |= 0x40; /* set DECAREN (decrementer auto-reload) bit */
    _PSP_SPR_SET(E300CORE_HID0, hid0);
#endif
   
    _bsp_e300dec_clk_en();

    return period;
}


/*ISR*********************************************************************
*
* Function Name    : _e300_decrementer_isr
* Returned Value   : void
* Comments         :
*    The decrementer ISR.  Needed because the decrementer WILL interrupt
*
*END**********************************************************************/

void _e300_decrementer_isr
    (
        pointer dummy
    )
{ 
#if BSP_E300DEC_SW_RELOAD == 1
    KERNEL_DATA_STRUCT_PTR  kernel_data;
    uint_32 period, dec;

    _GET_KERNEL_DATA(kernel_data);
    period = kernel_data->TIMER_HW_REFERENCE;

    _PSP_SPR_GET(dec, E300CORE_DEC);
    dec += period; /* setup next period countdown */
    _PSP_SPR_SET(E300CORE_DEC, dec);

    /* check for missed interrupt and eventually reinitialize the decrementer */
    if (dec>=period)
        _PSP_SPR_SET(E300CORE_DEC, period-1);
#endif

    _time_notify_kernel();
}


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _e300_decrementer_get_hwticks
* Returned Value  : Number of ticks
* Comments        :
*   This function will return the number of ticks elapsed since the last
* timer tick.
*
*END*---------------------------------------------------------------------*/

uint_32 _e300_decrementer_get_hwticks
    (
        pointer param
    )
{
    KERNEL_DATA_STRUCT_PTR  kernel_data;
    uint_32 period, hwticks;

    _GET_KERNEL_DATA(kernel_data);
    period = kernel_data->TIMER_HW_REFERENCE;

    _PSP_SPR_GET(hwticks, E300CORE_DEC);

#if BSP_E300DEC_SW_RELOAD == 1
    /* not a perfect solution, pending interrupt is not detected if hw autoreload is used */
    if (hwticks >= period) {
        return 0xffffffff - hwticks + period;
    }
#endif

    return period - 1 - hwticks;
}
